/*
* JBoss, Home of Professional Open Source
* Copyright 2009-11, Red Hat Middleware LLC, and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.riftsaw.engine.internal;
import java.io.File;
import java.util.Collections;
import java.util.jar.JarEntry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.utils.DOMUtils;
import org.riftsaw.engine.DeploymentUnit;
/**
* This class is responsible for managing the deployment of
* a (set of) BPEL process(es).
*
*/
public class DeploymentManager {
private static final int DEFAULT_VERSION = 0;
private static final Log LOG=LogFactory.getLog(DeploymentManager.class);
private String _deploymentFolder=System.getProperty("java.io.tmpdir");
/**
* The default constructor.
*/
public DeploymentManager() {
}
/**
* This method sets the location of the working folder used
* to expand the BPEL deployments.
*
* @param location The working folder location
*/
public void setDeploymentFolder(String location) {
_deploymentFolder = location;
if (LOG.isDebugEnabled()) {
LOG.debug("Set folder to: "+_deploymentFolder);
}
}
/**
* This method returns the location of the working folder used
* to expand the BPEL deployments.
*
* @return The folder location
*/
public String getDeploymentFolder() {
return (_deploymentFolder);
}
/**
* This method creates a list of deployment units representing the distinct
* deployments that are associated with the source BPEL processes provided
* in the deployment root. Distinct deployment units may be required if
* (for example) multiple versions of the same BPEL process are defined.
*
* @param deploymentName The overall deployment name
* @param deploymentRoot The root location containing the deployment descriptor
* and associated artifacts (bpel, wsdl, xsd, etc).
* @return The list of distinct BPEL deployment units
* @throws Exception Failed to retrieve deployment units
*/
public java.util.List<DeploymentUnit> getDeploymentUnits(String deploymentName,
java.io.File deploymentRoot) throws Exception {
java.util.List<DeploymentUnit> ret=new java.util.Vector<DeploymentUnit>();
if (deploymentRoot == null || !deploymentRoot.exists()) {
throw new java.lang.IllegalArgumentException(
"Deployment root was not supplied, or does not exist");
}
// Check if deployment root is an archive
boolean exploded=false;
if (deploymentRoot.isFile()) {
deploymentRoot = explodeJar(deploymentName, deploymentRoot);
exploded = true;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Deploy is exploded? "+exploded);
}
// Scan deployment for BPEL processes
java.util.List<java.io.File> processes=getBPELProcesses(deploymentRoot);
if (processes.size() == 1 && exploded) {
java.io.File newDir=new java.io.File(deploymentRoot.getParentFile(),
getDeploymentUnitName(deploymentName, processes.get(0)));
// If exists already, then delete
if (newDir.exists()) {
delete(newDir);
}
// Rename the deployment
if (deploymentRoot.renameTo(newDir)) {
ret.add(createDeploymentUnit(newDir));
} else {
// Log error and try to copy
LOG.error("Unable to rename deployment at '"+deploymentRoot+"' to '"+newDir+"'");
copy(deploymentRoot, newDir, processes.get(0).getName(),
getProcessLocalName(processes.get(0)));
ret.add(createDeploymentUnit(newDir));
}
} else if (processes.size() > 0) {
for (java.io.File bpel : processes) {
java.io.File newDir=getTemporaryFolder(getDeploymentUnitName(deploymentName, bpel));
// If exists already, then delete
if (newDir.exists()) {
delete(newDir);
}
copy(deploymentRoot, newDir, bpel.getName(),
getProcessLocalName(bpel));
ret.add(createDeploymentUnit(newDir));
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Get deployment units for name '"+deploymentName+
"' and root '"+deploymentRoot+" = "+ret);
}
return (ret);
}
/**
* This method returns the local name of the BPEL process contained
* in the supplied file.
*
* @param bpel The BPEL process file
* @return The process's local name
* @throws Exception Failed to get local name
*/
protected String getProcessLocalName(java.io.File bpel) throws Exception {
String ret=null;
java.io.InputStream is=new java.io.FileInputStream(bpel);
byte[] b=new byte[is.available()];
is.read(b);
is.close();
org.w3c.dom.Element proc=DOMUtils.stringToDOM(b);
ret = proc.getAttribute("name");
return (ret);
}
/**
* This method copies the contents of the 'from' folder
* to the 'to' folder, filtering out all BPEL processes except
* the named one.
*
* @param fromDir The source folder
* @param toDir The destination folder
* @param bpelFileName The BPEL process filename to retain
* @throws Exception Failed to copy
*/
protected void copy(java.io.File fromDir, java.io.File toDir, String bpelFileName, String processName)
throws Exception {
// Check that destination folder exists
if (!toDir.exists()) {
toDir.mkdirs();
}
for (java.io.File f : fromDir.listFiles()) {
if (f.isFile()) {
if (!f.getName().endsWith(".bpel") || f.getName().equals(bpelFileName)) {
// Copy the file
java.io.InputStream is=new java.io.FileInputStream(f);
java.io.FileOutputStream fos =
new java.io.FileOutputStream(new java.io.File(toDir, f.getName()));
byte[] b=new byte[is.available()];
is.read(b);
if (f.getName().equals("deploy.xml")) {
b = filterDeploymentDescriptor(b, processName);
}
fos.write(b);
fos.flush();
fos.close();
is.close();
}
} else if (f.isDirectory()) {
copy(f, new java.io.File(toDir, f.getName()), bpelFileName, processName);
}
}
}
protected byte[] filterDeploymentDescriptor(byte[] b, String processName)
throws Exception {
byte[] ret=b;
org.w3c.dom.Element deploy=DOMUtils.stringToDOM(b);
boolean changed=false;
org.w3c.dom.NodeList nl=deploy.getElementsByTagName("process");
for (int i=nl.getLength()-1; i >= 0; i--) {
if (nl.item(i) instanceof org.w3c.dom.Element) {
org.w3c.dom.Element proc=(org.w3c.dom.Element)nl.item(i);
String name=proc.getAttribute("name");
// Check if has prefix and remove
int index=name.indexOf(':');
if (index != -1) {
name = name.substring(index+1);
}
if (!name.equals(processName)) {
// Remove element
proc.getParentNode().removeChild(proc);
changed = true;
}
}
}
if (changed) {
ret = DOMUtils.domToBytes(deploy);
}
return(ret);
}
/**
* This method creates a deployment unit from the supplied root and its
* contained deployment descriptor.
*
* @param deploymentRoot The deployment root
* @return The deployment unit
*/
protected DeploymentUnit createDeploymentUnit(java.io.File deploymentRoot) {
DeploymentUnit ret=null;
// Locate the deployment descriptor
java.io.File deployFile=new java.io.File(deploymentRoot, "deploy.xml");
if (!deployFile.exists()) {
throw new IllegalArgumentException("Supplied deployment root does not contain a deploy.xml file");
}
// Get version from root name
String version="1";
String name=deploymentRoot.getName();
int index=name.lastIndexOf('-');
if (index != -1) {
version = name.substring(index+1);
name = name.substring(0, index);
}
ret = new DeploymentUnit(name, version, deployFile.lastModified(),
deployFile);
return (ret);
}
/**
* This method returns the BPEL process's deployment folder name.
*
* @param deploymentName The deployment name
* @param process The BPEL Process file
* @return The process's deployment folder name
*/
protected static String getDeploymentUnitName(String deploymentName, java.io.File process) {
String processName=process.getName().substring(0, process.getName().length()-5);
// Check if version has been specified, and if not, then append default version
boolean f_addDefaultVersion=false;
int index=processName.lastIndexOf('-');
if (index == -1) {
f_addDefaultVersion = true;
} else {
String ver=processName.substring(index+1);
try {
Integer.parseInt(ver);
} catch(Exception e) {
// Not a valid number, so set default version
f_addDefaultVersion = true;
}
}
if (f_addDefaultVersion) {
processName += "-"+DEFAULT_VERSION;
}
return (deploymentName+"_"+processName);
}
/**
* This method provides a temporary directory associated with the
* supplied deployment name.
*
* @param deploymentName The deployment name
* @return The temporary folder
*/
protected java.io.File getTemporaryFolder(String deploymentName) {
String destPath = _deploymentFolder
+ java.io.File.separatorChar
+ "riftsaw"
+ java.io.File.separatorChar
+ "rs"+System.currentTimeMillis()
+ java.io.File.separatorChar
+ deploymentName;
return (new java.io.File(destPath));
}
/**
* This method explodes the supplied jar into a temporary location
* associated with the deployment name.
*
* @param deploymentName The deployment name
* @param deploymentRoot The deployment archive
* @return The new deployment root
* @throws java.io.IOException Failed to explode the jar
*/
private java.io.File explodeJar(String deploymentName, java.io.File deploymentRoot)
throws java.io.IOException {
java.io.File ret = getTemporaryFolder(deploymentName);
if (LOG.isDebugEnabled()) {
LOG.debug("Exploding deployment name '"+deploymentName+"' at '"+deploymentRoot+"' to: "+ret);
}
// Recursive delete in case already exists
delete(ret);
ret.mkdirs();
java.util.jar.JarFile jarFile =
new java.util.jar.JarFile(deploymentRoot);
java.util.Enumeration<JarEntry> iter =
jarFile.entries();
while (iter.hasMoreElements()) {
JarEntry entry = iter.nextElement();
String entryPath = ret.getAbsolutePath()
+ java.io.File.separatorChar;
entryPath += entry.getName();
java.io.File entryFile =
new java.io.File(entryPath);
if (entry.isDirectory()) {
entryFile.mkdirs();
} else {
java.io.InputStream is =
jarFile.getInputStream(entry);
java.io.FileOutputStream fos =
new java.io.FileOutputStream(entryFile);
byte[] b =
new byte[is.available()];
is.read(b);
fos.write(b);
fos.flush();
fos.close();
is.close();
}
}
jarFile.close();
return (ret);
}
/**
* This method deletes the supplied file. If it
* represents a directory, then the operation will
* be performed recursively.
*
* @param file The file or directory to be deleted
*/
private void delete(final java.io.File file) {
if (file.isDirectory()) {
for (java.io.File f : file.listFiles()) {
delete(f);
}
}
file.delete();
}
/**
* This method returns the list of BPEL process files.
*
* @param deploymentRoot The deployment root
* @return The list of BPEL process files
*/
protected java.util.List<java.io.File> getBPELProcesses(java.io.File deploymentRoot) {
java.util.List<java.io.File> ret=new java.util.Vector<java.io.File>();
if (!deploymentRoot.isDirectory()) {
throw new IllegalArgumentException("Deployment root must be a directory");
}
findBPELProcesses(deploymentRoot, ret);
// Sort files by name (and therefore version)
Collections.sort(ret, new java.util.Comparator<java.io.File>() {
public int compare(File o1, File o2) {
return(o1.getName().compareTo(o2.getName()));
}
});
if (LOG.isDebugEnabled()) {
LOG.debug("Found BPEL processes under '"+deploymentRoot+"' are: "+ret);
}
return (ret);
}
/**
* This method recursively scans a directory structure to locate all of the
* BPEL processes.
*
* @param file The file/directory to check
* @param processes The list of BPEL processes
*/
protected void findBPELProcesses(java.io.File file, java.util.List<java.io.File> processes) {
if (file.isDirectory()) {
for (java.io.File f : file.listFiles()) {
findBPELProcesses(f, processes);
}
} else if (file.getName().endsWith(".bpel")) {
processes.add(file);
}
}
}